package exo4;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public final class Bibliotheque {
	private String nom;
	private final List<Ouvrage> ouvrages;
	private final List<Abonne> abonnes;

	public Bibliotheque(String nom) {
		this.nom = nom;
		this.ouvrages = new ArrayList<Ouvrage>();
		this.abonnes = new ArrayList<Abonne>();
	}

	public Bibliotheque(String nom, List<Ouvrage> ouvrages, List<Abonne> abonnes) {
		this.nom = nom;
		// Copies defensives : ne pas recuperer une structure de donnees dont
		// l'appelant possede la reference
		this.ouvrages = new ArrayList<Ouvrage>(ouvrages);
		this.abonnes = new ArrayList<Abonne>(abonnes);
	}

	public Bibliotheque(String nom, List<Ouvrage> ouvrages) {
		this.nom = nom;
		this.ouvrages = new ArrayList<Ouvrage>(ouvrages);
		this.abonnes = new ArrayList<Abonne>();
	}

	public String getNom() {
		return this.nom;
	}

	public boolean addOuvrage(Ouvrage ouvrage) {
		boolean resultat = false;
		if (ouvrage != null && !this.ouvrages.contains(ouvrage)) {
			this.ouvrages.add(ouvrage);
			resultat = true;
		}
		return resultat;
	}

	public boolean addOuvrages(List<Ouvrage> ouvrages) {
		boolean resultat = false;
		if (ouvrages != null && !this.ouvrages.containsAll(ouvrages)) {
			this.ouvrages.addAll(ouvrages);
			resultat = true;
		}
		return resultat;
	}

	public boolean removeOuvrage(Ouvrage ouvrage) {
		return this.ouvrages.remove(ouvrage);
	}

	public boolean addAbonne(Abonne abonne) {
		boolean resultat = false;
		if (abonne != null && !this.abonnes.contains(abonne)) {
			this.abonnes.add(abonne);
			resultat = true;
		}
		return resultat;
	}
	
	public void removeAbonne(Abonne abonne) {
		if (hasAbonne(abonne)){
			List<Ouvrage> resaEnCours = abonne.getReservations();
			for(Ouvrage resa : resaEnCours) {
				abonne.removeReservation(resa);
				resa.unsetReserve();
			}
			List<Ouvrage> empruntsEnCours = abonne.getEmprunts();
			for(Ouvrage emprunt : empruntsEnCours) {
				abonne.removeEmprunt(emprunt);
				emprunt.setRetour();
			}
		}
	}
	
	public boolean addAbonnes(List<Abonne> abonnes) {
		boolean resultat = false;
		if (abonnes != null && !this.abonnes.containsAll(abonnes)) {
			this.abonnes.addAll(abonnes);
			resultat = true;
		}
		return resultat;
	}
	
	public List<Abonne> getAbonnes(){
		return new ArrayList<Abonne>(this.abonnes);
	}

	public boolean hasOuvrage(Ouvrage ouvrage) {
		return this.ouvrages.contains(ouvrage);
	}

	public boolean hasAbonne(Abonne abonne) {
		return this.abonnes.contains(abonne);
	}

	public boolean reserver(Ouvrage ouvrage, Abonne abonne) {
		boolean resultat = false;
		if (ouvrage != null && abonne != null) { // On peut se passer de ce
													// test, on pourrait avoir
													// un NullPointerException
													// dans ce cas.
			if (hasOuvrage(ouvrage) && hasAbonne(abonne)) {
				if (!ouvrage.isReserve() && ouvrage.isDisponible()) {
					resultat = abonne.addReservation(ouvrage);
					// Soit la resa s'est bien passee, soit l'ouvrage n'est pas
					// marque reserve mais se trouve
					// dans la liste des resa de l'abonne...
					ouvrage.setReserve();
				}
			}
		}
		return resultat;
	}

	public void annulerReservation(Ouvrage ouvrage, Abonne abonne) {
		if (ouvrage != null && abonne != null) {
			if (hasOuvrage(ouvrage) && hasAbonne(abonne)) {
				abonne.removeReservation(ouvrage);
				ouvrage.unsetReserve();
			}
		}
	}

	public boolean emprunter(Ouvrage ouvrage, Abonne abonne) {
		boolean resultat = false;
		if (ouvrage != null && abonne != null) {
			if (hasOuvrage(ouvrage) && hasAbonne(abonne)) {
				if (ouvrage.isDisponible()) {
					if (!ouvrage.isReserve()
							|| (ouvrage.isReserve() && abonne.aReserve(ouvrage)))
						resultat = abonne.addEmprunt(ouvrage);
					if (resultat) {
						ouvrage.setEmprunteur(abonne);
						abonne.removeReservation(ouvrage);
					}
				}
			}
		}
		return resultat;
	}

	public boolean restituer(Ouvrage ouvrage) {
		boolean resultat = false;
		if (ouvrage != null && hasOuvrage(ouvrage)
				&& ouvrage.getEmprunteur() != null) {
			Abonne abonne = ouvrage.getEmprunteur();
			resultat = abonne.removeEmprunt(ouvrage);
			if (!resultat) {
				for (Abonne abo : this.abonnes) {
					if (abo.aEmprunte(ouvrage)) {
						resultat = abo.removeEmprunt(ouvrage);
						break;
					}
				}
			}
			ouvrage.setRetour();
		}
		return resultat;
	}

	public List<Ouvrage> ouvragesDisponibles() {
		List<Ouvrage> resultat = new ArrayList<Ouvrage>();
		for (Ouvrage ouvrage : this.ouvrages) {
			if (ouvrage.isDisponible())
				resultat.add(ouvrage);
		}
		return resultat;
	}

	public List<Ouvrage> ouvragesReserves() {
		List<Ouvrage> resultat = new ArrayList<Ouvrage>();
		for (Ouvrage ouvrage : this.ouvrages) {
			if (ouvrage.isReserve())
				resultat.add(ouvrage);
		}
		return resultat;
	}

	public List<Ouvrage> ouvragesEmpruntes() {
		List<Ouvrage> resultat = new ArrayList<Ouvrage>();
		for (Ouvrage ouvrage : this.ouvrages) {
			if (ouvrage.getEmprunteur() != null)
				resultat.add(ouvrage);
		}
		return resultat;
	}

	public List<Abonne> abonnesAyantReserve() {
		List<Abonne> resultat = new ArrayList<Abonne>();
		for (Abonne abonne : this.abonnes){
			if (!abonne.getReservations().isEmpty())
				resultat.add(abonne);
		}
		return resultat;
	}
	
	public List<Abonne> abonnesAyantEmprunte() {
		List<Abonne> resultat = new ArrayList<Abonne>();
		for (Abonne abonne : this.abonnes){
			if (!abonne.getEmprunts().isEmpty())
				resultat.add(abonne);
		}
		return resultat;
	}
	
	public List<Abonne> abonneSansResaSansEmprunt() {
		List<Abonne> resultat = new ArrayList<Abonne>();
		for (Abonne abonne : this.abonnes){
			if (abonne.getReservations().isEmpty() && abonne.getEmprunts().isEmpty())
				resultat.add(abonne);
		}
		return resultat;
	}
	
	public List<Media> getListeMedia(){
		return Arrays.asList(Media.values());
	}
	
	public List<Integer[]> nbOuvragesParTypeDispoEmprunteReserve(){
		List<Integer[]> result = new ArrayList<Integer[]>();
		
		Integer[] cdDispoEmpRes = new Integer[3];
		Integer[] cleDispoEmpRes = new Integer[3];
		Integer[] dvdDispoEmpRes = new Integer[3];
		Integer[] papierDispoEmpRes = new Integer[3];
		
		for (int i = 0; i < 3; i++)  {
			cdDispoEmpRes[i] = new Integer(0);
			cleDispoEmpRes[i] = new Integer(0);
			dvdDispoEmpRes[i] = new Integer(0);
			papierDispoEmpRes[i] = new Integer(0);
		}
		
		for(Ouvrage ouv : this.ouvrages){
			switch (ouv.getMedia()) {
				case CD :
					if (ouv.isDisponible())
						cdDispoEmpRes[0]++;
					else 
						cdDispoEmpRes[1]++;
					if (ouv.isReserve())
						cdDispoEmpRes[2]++;
					break;
				case  CLE_USB :
					if (ouv.isDisponible())
						cleDispoEmpRes[0]++;
					else 
						cleDispoEmpRes[1]++;
					if (ouv.isReserve())
						cleDispoEmpRes[2]++;
					break;
				case DVD :
					if (ouv.isDisponible())
						dvdDispoEmpRes[0]++;
					else 
						dvdDispoEmpRes[1]++;
					if (ouv.isReserve())
						dvdDispoEmpRes[2]++;
					break;
				case PAPIER :
					if (ouv.isDisponible())
						papierDispoEmpRes[0]++;
					else 
						papierDispoEmpRes[1]++;
					if (ouv.isReserve())
						papierDispoEmpRes[2]++;
					break;
				default :
					break;
			}
		}
		result.add(cdDispoEmpRes);
		result.add(cleDispoEmpRes);
		result.add(dvdDispoEmpRes);
		result.add(papierDispoEmpRes);
		
		return result;
	}
}
